7: Verify Issued Credential
Sending proof request
Once the connection is established, the Verifier can send a proof request.
There are optional restrictions and additional fields that can be added to the proof request, which are beyond the scope of this simple example. For more information, please see our docs for restrictions on proofs.
curl -X 'POST' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/send-request' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"protocol_version": "v2",
"comment": "Demo",
"type": "indy",
"indy_proof_request": {
"requested_attributes": {
"holder_surname": { "name": "Surname", "restrictions":[]},
"holder_name": { "name": "Name", "restrictions": []},
"holder_age": { "name": "Age", "restrictions": []}
},
"requested_predicates": {}
},
"connection_id": "5ef9f4e0-9f98-4e43-aef7-de11da2ccd40"
}'
Response:
{
"connection_id": "5ef9f4e0-9f98-4e43-aef7-de11da2ccd40",
"created_at": "2023-11-22T10:12:20.755226Z",
"error_msg": null,
"parent_thread_id": "ce5b5597-d3fa-437b-a857-0927694cc4b9",
"presentation": null,
"presentation_request": {
"name": null,
"non_revoked": null,
"nonce": "1040329690360437135931695",
"requested_attributes": {
"holder_surname": {
"name": "Surname",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_name": {
"name": "Name",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_age": {
"name": "Age",
"names": null,
"non_revoked": null,
"restrictions": []
}
},
"requested_predicates": {},
"version": null
},
"proof_id": "v2-57c1bf16-1fc3-4506-b672-8b11580c4920",
"protocol_version": "v2",
"role": "verifier",
"state": "request-sent",
"thread_id": "ce5b5597-d3fa-437b-a857-0927694cc4b9",
"updated_at": "2023-11-22T10:12:20.755226Z",
"verified": null
}
Note that the verifier will now have what's called a presentation exchange record in state: request-sent. Pending presentation records can be viewed by calling
GET /v1/verifier/proofs
, and completed presentation exchange records are deleted by default, but can be preserved by adding an optionalsave_exchange_record=True
field to the request.
Holder responds to proof request
The holder would have received a webhook event on topic proofs
, indicating they have received a request. Example webhook:
{
"wallet_id": "4e0c70fb-f2ad-4f59-81f3-93d8df9b977a",
"topic": "proofs",
"origin": "multitenant",
"payload": {
"connection_id": "ab1cc0fe-d797-429c-be36-7830a79d52a1",
"created_at": "2023-11-16T09:59:19.612647Z",
"error_msg": null,
"parent_thread_id": null,
"presentation": null,
"presentation_request": null,
"proof_id": "v2-ba39fb0f-4dff-4bce-8db0-fdad3432cc7d",
"protocol_version": "v2",
"role": "prover",
"state": "request-received",
"thread_id": "aea706fd-5492-4ed7-ab1c-1bb9ff309926",
"updated_at": "2023-11-16T09:59:19.612647Z",
"verified": null
}
}
The Holder will now see a presentation exchange record when they call GET
on the /v1/verifier/proofs
endpoint:
curl -X 'GET' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/proofs' \
-H 'accept: application/json'
-H 'x-api-key: tenant.<holder token>' \
Response:
[
{
"connection_id": "ab1cc0fe-d797-429c-be36-7830a79d52a1",
"created_at": "2023-11-16T09:59:19.612647Z",
"error_msg": null,
"parent_thread_id": "aea706fd-5492-4ed7-ab1c-1bb9ff309926",
"presentation": null,
"presentation_request": {
"name": "Proof Request",
"non_revoked": null,
"nonce": "234234",
"requested_attributes": {
"holder_surname": {
"name": "surname",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_name": {
"name": "name",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_age": {
"name": "age",
"names": null,
"non_revoked": null,
"restrictions": []
}
},
"requested_predicates": {},
"version": "1.0"
},
"proof_id": "v2-ba39fb0f-4dff-4bce-8db0-fdad3432cc7d",
"protocol_version": "v2",
"role": "prover",
"state": "request-received",
"thread_id": "aea706fd-5492-4ed7-ab1c-1bb9ff309926",
"updated_at": "2023-11-16T09:59:19.612647Z",
"verified": null
}
]
Note that their role indicates prover
, and the state is request-received
. Prover is the term used for a holder in a
proof exchange. Additionally, note that the prover and the verifier have different proof_id
references for the same
proof interaction.
The Holder/Prover can now check which credentials match the fields that are requested in the proof request by using the
proof_id
and making a call to /v1/verifier/proofs/{proof_id}/credentials
.
NOTE: If the call is successful, but returns an empty list
[]
, it means that the credentials of theHolder
do not match the requested fields in the proof request.
curl -X 'GET' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/proofs/v2-93e29a31-5eab-4091-9d1d-f27220f445fd/credentials' \
-H 'accept: application/json'
Response:
NOTE: This response is a list. Each object in this list corresponds to a credential, matching the requested attributes. In this case, the response has only one object, meaning all the requested attributes are found in one credential.
[
{
"cred_info": {
"attrs": {
"Age": "25",
"Surname": "Holder",
"Name": "Alice"
},
"cred_def_id": "2hPti9M3aQqsRCy8N6jrDB:3:CL:10:Demo Person",
"cred_rev_id": null,
"referent": "10e6b03f-2b60-431a-9634-731594423120",
"rev_reg_id": null,
"schema_id": "QpSW24YVf61A3sAWxArfF6:2:Person:0.1.0"
},
"interval": null,
"presentation_referents": ["holder_name", "holder_age", "holder_surname"]
}
]
We can now use the referent
(the referent is the holder's reference to their credential id) from the response above to
accept the proof request:
curl -X 'POST' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/accept-request' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"proof_id": "v2-614a1035-c855-417d-8a8e-0c824bb6ab0f",
"type": "indy",
"indy_presentation_spec": {
"requested_attributes": {
"holder_surname": {
"cred_id": "10e6b03f-2b60-431a-9634-731594423120",
"revealed": true
},
"holder_name": {
"cred_id": "10e6b03f-2b60-431a-9634-731594423120",
"revealed": true
},
"holder_age": {
"cred_id": "10e6b03f-2b60-431a-9634-731594423120",
"revealed": true
}
},
"requested_predicates": {},
"self_attested_attributes": {},
},
"dif_presentation_spec": {}
}'
Click to see Response
{
"connection_id": "43264326-57a7-4ef4-aa65-906dd9c15961",
"created_at": "2023-11-22T06:11:46.956062Z",
"error_msg": null,
"parent_thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"presentation": {
"identifiers": [
{
"cred_def_id": "2hPti9M3aQqsRCy8N6jrDB:3:CL:10:Demo Person",
"rev_reg_id": null,
"schema_id": "QpSW24YVf61A3sAWxArfF6:2:Person:0.1.0",
"timestamp": null
}
],
"proof": {
"aggregated_proof": {
"c_hash": "3219322627487542487718476352817657516298296911958347169452423884069641575597",
"c_list": [
[2,221,130,248,96,145,3,19,86,111,75,202,101,190,166,101,222,83,181,133,88,134,...]
]
},
"proofs": [
{
"non_revoc_proof": null,
"primary_proof": {
"eq_proof": {
"a_prime": "92597261364394573165798933206533873274818813554675314388834609214520451243506734698448...",
"e": "13957611453314484376536548594867291175484947973045451726862617289480834912062823268784462725...",
"m": {
"master_secret": "113138788366524810935384910670667169176189186849867816774981002913273272990310..."
},
"m2": "9527966448141582634179217567781606687146704440935821032302777460114542256740868407661366941...",
"revealed_attrs": {
"holder_age": "25",
"holder_name": "27034640024117331033063128044004318218486816931520886405535659934417438781507",
"holder_surname": "108415864455171922802944099373800995974825385451497756533671241088029831060565"
},
"v": "13310269669535827858183383537992882823597862340798892194315939604086055386082234517559247559..."
},
"ge_proofs": []
}
}
]
},
"requested_proof": {
"predicates": {},
"revealed_attr_groups": null,
"revealed_attrs": {
"holder_surname": {
"encoded": "108415864455171922802944099373800995974825385451497756533671241088029831060565",
"raw": "Holder",
"sub_proof_index": 0
},
"holder_name": {
"encoded": "27034640024117331033063128044004318218486816931520886405535659934417438781507",
"raw": "Alice",
"sub_proof_index": 0
},
"holder_age": {
"encoded": "25",
"raw": "25",
"sub_proof_index": 0
}
},
"self_attested_attrs": {},
"unrevealed_attrs": {}
}
},
"presentation_request": {
"name": "Proof Request",
"non_revoked": null,
"nonce": "234234",
"requested_attributes": {
"holder_surname": {
"name": "surname",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_name": {
"name": "name",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_age": {
"name": "age",
"names": null,
"non_revoked": null,
"restrictions": []
}
},
"requested_predicates": {},
"version": "1.0"
},
"proof_id": "v2-614a1035-c855-417d-8a8e-0c824bb6ab0f",
"protocol_version": "v2",
"role": "prover",
"state": "presentation-sent",
"thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"updated_at": "2023-11-22T06:28:01.553809Z",
"verified": null
}
If the proof request is valid, then the verification will complete automatically. Once again, we wait for the exchange
to be completed by listening to webhooks. Here is an example webhook event for the topic proofs
in the done
state.
{
"wallet_id": "92893658-fe2d-4f2d-8268-a60a601945a9",
"topic": "proofs",
"origin": "multitenant",
"payload": {
"connection_id": "5ef9f4e0-9f98-4e43-aef7-de11da2ccd40",
"created_at": "2023-11-22T06:11:46.897536Z",
"error_msg": null,
"parent_thread_id": null,
"presentation": null,
"presentation_request": null,
"proof_id": "v2-e83d3d75-9eb1-4d54-a321-7d0d5c5d286e",
"protocol_version": "v2",
"role": "verifier",
"state": "done",
"thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"updated_at": "2023-11-22T06:28:01.704464Z",
"verified": true
}
}
Verifier can get the proof with all its data by making the following call:
curl -X 'GET' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/proofs' \
-H 'accept: application/json'
Click to see Response
[
{
"connection_id": "5ef9f4e0-9f98-4e43-aef7-de11da2ccd40",
"created_at": "2023-11-22T06:11:46.897536Z",
"error_msg": null,
"parent_thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"presentation": {
"identifiers": [
{
"cred_def_id": "2hPti9M3aQqsRCy8N6jrDB:3:CL:10:Demo Person",
"rev_reg_id": null,
"schema_id": "QpSW24YVf61A3sAWxArfF6:2:Person:0.1.0",
"timestamp": null
}
],
"proof": {
"aggregated_proof": {
"c_hash": "3219322627487542487718476352817657516298296911958347169452423884069641575597",
"c_list": [
[2,221,130,248,96,145,3,19,86,111,75,202,101,190,166,101,222,83,181,133,88,134,152,205,154,157,...]
]
},
"proofs": [
{
"non_revoc_proof": null,
"primary_proof": {
"eq_proof": {
"a_prime": "9259726136439457316579893320653387327481881355467531438883460921452045124350673469620...",
"e": "1395761145331448437653654859486729117548494797304545172686261728948083491206282326878446272...",
"m": {
"master_secret": "11313878836652481093538491067066716917618918684986781677498100291327327294712..."
},
"m2": "952796644814158263417921756778160668714670444093582103230277746011454225674086840766136694...",
"revealed_attrs": {
"holder_age": "25",
"holder_name": "27034640024117331033063128044004318218486816931520886405535659934417438781507",
"holder_surname": "108415864455171922802944099373800995974825385451497756533671241088029831060565"
},
"v": "1331026966953582785818338353799288282359786234079889219431593960408605538608223451755924755..."
},
"ge_proofs": []
}
}
]
},
"requested_proof": {
"predicates": {},
"revealed_attr_groups": null,
"revealed_attrs": {
"holder_surname": {
"encoded": "108415864455171922802944099373800995974825385451497756533671241088029831060565",
"raw": "Holder",
"sub_proof_index": 0
},
"holder_name": {
"encoded": "27034640024117331033063128044004318218486816931520886405535659934417438781507",
"raw": "Alice",
"sub_proof_index": 0
},
"holder_age": {
"encoded": "25",
"raw": "25",
"sub_proof_index": 0
}
},
"self_attested_attrs": {},
"unrevealed_attrs": {}
}
},
"presentation_request": {
"name": "Proof Request",
"non_revoked": null,
"nonce": "234234",
"requested_attributes": {
"holder_surname": {
"name": "surname",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_name": {
"name": "name",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_age": {
"name": "age",
"names": null,
"non_revoked": null,
"restrictions": []
}
},
"requested_predicates": {},
"version": "1.0"
},
"proof_id": "v2-e83d3d75-9eb1-4d54-a321-7d0d5c5d286e",
"protocol_version": "v2",
"role": "verifier",
"state": "done",
"thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"updated_at": "2023-11-22T06:28:01.704464Z",
"verified": true
}
]
Hooray! 🥳🎉 Well done, you now know how to issue and verify credentials!
If you would like to learn about revoking credentials, please proceed to the next section: Revoking Credentials.